/*==============================================================================
 
 JUCE library : Starting point code, v1.26
 Copyright 2005 by Julian Storer. [edited by haydxn, 3rd April 2007]
 Modified to Pong Game by David Rowland 2008
 
 ==============================================================================*/


#ifndef _MAINCOMPONENT_H_
#define _MAINCOMPONENT_H_


#include <juce/juce.h>
#include "SettingsComp.h"
#include "AudioFilePlayer.h"
#include "BitwiseFontResource.h"

#define pauseTime 3000
#define SOUNDS_DIRECTORY "../../Sounds"

//==============================================================================
// Main component class definition
//==============================================================================
/**		A Pong Game class to create the classic two player game.
		
		@todo	Add embedded "Bitwise" font to application
		@todo	Embed sounds into application so users don't have to have the Sounds
				directory present
		@todo	Make ball angle change if it hits edge of bat for a more interesting game
		@todo	Add animation to label changes to give a more game-like feel
		@todo	Separate buttons and scores into a separate component so the ball has 
				its own dedicated play area.
  */
class MainComponent  :  public Component,
						public ButtonListener,
						public Timer,
						public AudioIODeviceCallback,
						public KeyListener
	{
	private:
		// Graphical Objects
		TextButton* bat1;
		TextButton* bat2;
		TextButton* playPauseButton;
		TextButton* stopButton;
		TextButton* settingsButton;
		Label* score1;
		Label* score2;
		Label* displayLabel;
		Label* winnersLabel;
		TextButton* ball;
		
//		SerialisedFont* bitwiseFont;
				
		SettingsComp* settingsComp;
		
		// Variables
		float x, y, dx, dy, ballAngle, prevdx, prevdy;
		
		unsigned int timeToStart;
		int timeLeft;
		
		// Application Flags
		bool isPaused;
		bool waitingForKeyPress;
		bool hasKeyBeenPressed;	
		
		bool bounced;
		bool leftSideHit;
		bool rightSideHit;
		
		bool hasGotKeyFocus;
				
		// Audio Objects
		AudioDeviceManager audioDeviceManager;
		AudioSourcePlayer audioSourcePlayer;
		MixerAudioSource mixerSource;
		OwnedArray<AudioFilePlayer> filePlayers;
		OwnedArray<File> audioFiles;
		
		/**	This enumeration allows access to the sound files by name without having 
			to know their array position.	
		 */
		enum soundFiles
		{
			serveHitSound = 0,
			tableHitSound = 1,
			batHitSound = 2,
			crowdCheerSound = 3,
			crowdBooSound = 4,
			winningSound = 5
		};
		
		// Add tool tips to the application
		TooltipWindow tooltipWindow;		
		
	public:
		//==============================================================================
		/**		This sets up the main component.
				This include the interface, the initial behaviour and the audio.
		 */
		MainComponent ()
						:waitingForKeyPress(false),
						hasKeyBeenPressed(false),
						isPaused(false),
						bounced(false),
						leftSideHit(false),
						rightSideHit(false),
						hasGotKeyFocus(false),
						timeLeft(0)
		{
			// Instantiate the font
			Font* bitwiseFont = 0;
			MemoryInputStream fontStream (bitwiseFontResource::bitwiseFontBinary, bitwiseFontResource::bitwiseFontBinary_Size, false);
			Typeface* bitwiseTypeface = new Typeface (fontStream);
			bitwiseFont = new Font (*bitwiseTypeface);
			delete bitwiseTypeface;			// Because the font stores it's own typeface
			bitwiseFont->setHeight (30.0f);
			
			// Instantiate the settings window
			settingsComp = new SettingsComp();
			
			// Make sure the component can have keyboard focus
			addKeyListener(this);
			this->setWantsKeyboardFocus(true);

			
			// Set up the bats
			addAndMakeVisible( bat1 = new TextButton(String::empty, T("First bat")) );
			bat1->setConnectedEdges(3);
			
			addAndMakeVisible( bat2 = new TextButton(String::empty, T("Second bat")) );
			bat2->setConnectedEdges(3);
			
			// Set up the ball
			addAndMakeVisible( ball = new TextButton(String::empty, T("Ball")) );
			ball->setColour(TextButton::buttonColourId, (Colours::black));
			
			
			// Set up label components to display messages to the user
			addAndMakeVisible( displayLabel = new Label(T("User Notification Label"),
													    T("Press Play to\nBegin")) );
			displayLabel->setInterceptsMouseClicks(false, false);
			displayLabel->setFont (*bitwiseFont/*Font ("Bitwise", 30.0000f, Font::plain)*/);
			displayLabel->setJustificationType (Justification::centred);
			displayLabel->setEditable (false, false, false);
			displayLabel->setColour (Label::textColourId, Colours::black);
			displayLabel->setColour (Label::backgroundColourId, Colour (0xa0ffffff));
			
			bitwiseFont->setHeight (15.0f);
			addChildComponent( winnersLabel = new Label(T("Winners Notification Label"),
														T("Press any key to play again...")) );
			winnersLabel->setInterceptsMouseClicks(false, false);
			winnersLabel->setFont (*bitwiseFont /*Font ("Bitwise", 15.0000f, Font::plain)*/);
			winnersLabel->setJustificationType (Justification::centred);
			winnersLabel->setEditable (false, false, false);
			winnersLabel->setColour (Label::textColourId, Colours::black);
			winnersLabel->setColour (Label::backgroundColourId, Colour (0x00ffffff));			
			
			
			// Set up the buttons
			addAndMakeVisible( settingsButton = new TextButton( T("Settings"),
															    T("Change the Game Settings 'G'")) );
			settingsButton->addButtonListener(this);
			settingsButton->setConnectedEdges(3);
			
			addAndMakeVisible( playPauseButton = new TextButton(T("Play"),
																T("Play/Pause 'P'")) );
			playPauseButton->addButtonListener(this);
			playPauseButton->setConnectedEdges(2);
			playPauseButton->setColour(TextButton::buttonColourId, Colours::lightgreen);
			playPauseButton->setColour(TextButton::buttonOnColourId, Colours::darkgreen);
			playPauseButton->setClickingTogglesState(true);
			
			addAndMakeVisible( stopButton = new TextButton(T("Stop"),
														   T("Stop and Reset 'S'")) );
			stopButton->addButtonListener(this);
			stopButton->setConnectedEdges(1);
			stopButton->setColour(TextButton::buttonColourId, Colours::palevioletred);
			stopButton->setColour(TextButton::buttonOnColourId, Colours::red);
			
						
			// Set up the score labels
			bitwiseFont->setHeight (30.0f);
			addAndMakeVisible( score1 = new Label (T("Player 1 Score"),
												   T("0")) );
			score1->setFont (*bitwiseFont /*Font ("Bitwise", 30.0000f, Font::plain)*/);
			score1->setJustificationType (Justification::centred);
			score1->setEditable (false, false, false);
			score1->setColour (TextEditor::textColourId, Colours::black);
			
			addAndMakeVisible( score2 = new Label (T("Player 2 Score"),
												   T("0")) );
			score2->setFont (*bitwiseFont /*Font ("Bitwise", 30.0000f, Font::plain)*/);
			score2->setJustificationType (Justification::centred);
			score2->setEditable (false, false, false);
			score2->setColour (TextEditor::textColourId, Colours::black);
			
						
			// This randomizes the initial direction of the ball
			ballAngle = (0.5 + ((Random::getSystemRandom().nextFloat()) / 2));
			
			// Keep a note of the non-zero ball vector to avoid pausing issues 
			prevdx = settingsComp->ballSpeedSlider->getValue();
			prevdy = (settingsComp->ballSpeedSlider->getValue() * ballAngle);
			
			//===============================================================================================
			// Set up the audio
			//===============================================================================================
			
			// Initialise the audio device
			const String error (audioDeviceManager.initialise (1, /* number of input channels */
															   2, /* number of output channels */
															   0, /* no XML settings.. */
															   true  /* select default device on failure */));
			if (error.isNotEmpty())
			{
				AlertWindow::showMessageBox (AlertWindow::WarningIcon,
											 T("Pong Game"),
											 T("Couldn't open an output device!\n\n") + error);
			}
			else
			{
				// Connect the mixer to our source player.
				audioSourcePlayer.setSource (&mixerSource);
				
				// Start the IO device pulling its data from our callback..
				audioDeviceManager.setAudioCallback (this);
				
				// Find the Sounds directory
				File appDirectory = File::getSpecialLocation(File::currentApplicationFile).getParentDirectory();
				File soundsDirectory = appDirectory.getChildFile(T(SOUNDS_DIRECTORY));
				
				// Check to see if the sounds directory is there
				if (!soundsDirectory.isDirectory())
					DBG_PRINTF(( T("\nNo Sounds Directory Found\n") ));

				DBG_PRINTF(( String("App Directory: ") << appDirectory.getFullPathName() ));
				DBG_PRINTF(( String("Sounds Directory: ") << soundsDirectory.getFullPathName() ));
				
				// find all the .wav files in our sounds directory
				soundsDirectory.findChildFiles(audioFiles, File::findFiles, false, T("*.wav"));
				
				// iterate through our files, making a player for each
				for (int i = 0; i < audioFiles.size(); i++ )
				{
					String soundFilePath = audioFiles[i]->getFullPathName();
					DBG_PRINTF(( soundFilePath ));
					
					// a new instance of one of our player objects
					// initialised with the path of this sound file
					filePlayers.add (new AudioFilePlayer(soundFilePath));
					
					// plug it in to our mixer
					mixerSource.addInputSource (filePlayers[i], false);
				}
			}
			
			// Start the timer callback to move the bats and ball
			// 40ms was chosen to give a smooth display at 25fps whilst
			// not taking up unnecessary system resources
			startTimer(40);
		}
		
		/// Destructor
		~MainComponent ()
		{
			audioDeviceManager.setAudioCallback (0);
			
			// (NB  "filePlayers" is an OwnedArray so it and its elements are deleted for us )
			
			audioSourcePlayer.setSource (0);
			
			// This has to be deleted manually to avoid a memory leak
			deleteAndZero(settingsComp);
			
			deleteAllChildren();   // This will remove all the contained components, and delete
			// the objects for you.
		}
		
		//==============================================================================
		/** Position graphical objects relative to the components size.
		 
			The bats and ball are reset when the window is resized to avoid a
		    user dragging the window over the ball and scoring a point.
		    This also makes it fairer when restarting the game so the ball 
			is not too close to one edge.
		 */
		void resized ()
		{
			bat1->setBounds (30, getHeight()/2 - 25, 5, 50);
			bat2->setBounds ((getWidth() - 30 - 5), getHeight()/2 - 25, 5, 50);
			
			x = getWidth()/2 - 10;
			y = getHeight()/2 -10;
			ball->setBounds(x, y, 20, 20);

			score1->setBounds (10, getHeight() - 35, 30, 30);
			score2->setBounds (getWidth() - 10 - 30, getHeight() - 35, 30, 30);
			
			settingsButton->setBounds((getWidth() / 2) - ((70) / 2), getHeight() - 10 - 24, 70, 24);
			playPauseButton->setBounds((getWidth() / 2) - ((170) / 2), getHeight() - 10 - 24, 50, 24);
			stopButton->setBounds((getWidth() / 2) + ((70) / 2), getHeight() - 10 - 24, 50, 24);
			
			displayLabel->setBounds(0, 0, getWidth(), getHeight());
			winnersLabel->setBounds(0, (getHeight()/2 + 30), getWidth(), 20);
		}
		
		/// Paint background graphics
		void paint (Graphics& g)
		{
			g.setColour (Colour (0xffffdddd));
			
			g.fillRect ((getWidth() / 2) - ((2) / 2), 0, 2, proportionOfHeight (1.0000f));
			g.drawEllipse ((float) ((getWidth() / 2) - ((60) / 2)), (float) ((getHeight() / 2) - ((60) / 2)), 60.0f, 60.0f, 2.0000f);
			g.fillEllipse ((float) ((getWidth() / 2) - ((20) / 2)), (float) ((getHeight() / 2) - ((20) / 2)), 20.0f, 20.0f);
		}
		
		
		//==============================================================================
		// Timer callback
		//==============================================================================
		
		void timerCallback ()
		{			
			// Let the main component grab keyboard focus initially
			if(!hasGotKeyFocus)
			{
				grabKeyboardFocus();
				hasGotKeyFocus = true;
				DBG_PRINTF(( String("Does the component have keyboard focus?") << String(hasKeyboardFocus(false)) ));
			}
			
			int requiredToWin = settingsComp->pointsToWinSlider->getValue();
			
			// If not waiting for a key press carry on with the game
			if (! waitingForKeyPress)
			{	
				// Check to see if the game should be paused
				if (isPaused == true)
				{
					// Empty the display label and display it ready for the count down
					displayLabel->setText(String::empty, false);
					displayLabel->setVisible(true);
					
					// Disable the pause, stop and settings buttons
					playPauseButton->setEnabled(false);
					settingsButton->setEnabled(false);
					stopButton->setEnabled(false);
					
					timeLeft = (timeToStart - Time::getMillisecondCounter());
					
					// Count down the pause
					if ( (timeLeft > 2000) && (timeLeft <= 3000))
						displayLabel->setText(T("3"), false);
					else if ( (timeLeft > 1000) && (timeLeft <= 2000))
						displayLabel->setText(T("2"), false);
					else if ( (timeLeft >= 0) && (timeLeft <= 1000))
						displayLabel->setText(T("1"), false);
					
					// If the required time has elapsed let the callback continue
					if (Time::getMillisecondCounter() >= timeToStart)
					{
						removeDisplayText();
						isPaused = false;
						
						// Enable the pause, settings and stop buttons
						playPauseButton->setEnabled(true);
						settingsButton->setEnabled(true);
						stopButton->setEnabled(true);
						
						// Play the serve sound
						playSound(serveHitSound);
					}
				}
				else if (isPaused == false)
				{
					// Move the bats and then the ball
					moveBats();
					moveBall();
					
					// Check to see if one of the sides was hit and act acordingly
					if (leftSideHit)
					{
						// Increase score and play correct sound
						score2->setText(String((score2->getText().getIntValue()) + 1), false);						
						if(score2->getText().getIntValue() < requiredToWin)
						{
							playSound(crowdBooSound);
							// Set the pause flag and the time to pause for
							isPaused = true;
							timeToStart = (Time::getMillisecondCounter() + pauseTime);
						}
						else
							playSound(winningSound);
						
						// Set co-ordinates to re-centre ball and set a new angle
						x = getWidth()/2 - 10;
						y = getHeight()/2 - 10;
						ball->setTopLeftPosition ((int) x, (int) y);
						ballAngle = (0.5 + ((Random::getSystemRandom().nextFloat()) / 2));
					}
					else if (rightSideHit)
					{
						// Increase score and play correct sound
						score1->setText(String((score1->getText().getIntValue()) + 1), false);
						if(score1->getText().getIntValue() < requiredToWin)
						{
							playSound(crowdCheerSound);
							// Set the pause flag and the time to pause for
							isPaused = true;
							timeToStart = (Time::getMillisecondCounter() + pauseTime);
						}
						else
							playSound(winningSound);
							
						// Set co-ordinates to re-centre ball
						x = getWidth()/2 - 10;
						y = getHeight()/2 - 10;
						ball->setTopLeftPosition ((int) x, (int) y);
						ballAngle = (0.5 + ((Random::getSystemRandom().nextFloat()) / 2));
					}
					else if (bounced)
					{
						playSound(tableHitSound);
					}
					
					// Check scores for maximum
					if ( (score1->getText().getIntValue() == requiredToWin)
						|| (score2->getText().getIntValue() == requiredToWin) )
						waitingForKeyPress = true;
				}
			}
			else if (waitingForKeyPress && !hasKeyBeenPressed)
			{
				DBG("Waiting for key press to continue...");
				
				// Player 1 wins
				if( score1->getText().getIntValue() >= requiredToWin )
				{
					DBG("Player 1 wins");
					setDisplayText("Player 1 Wins\nWell Done!");
					winnersLabel->setVisible(true);
					
					// Disable the pause, stop and settings buttons
					playPauseButton->setEnabled(false);
					settingsButton->setEnabled(false);
					stopButton->setEnabled(false);
				}
				// Player 2 wins
				else if( score2->getText().getIntValue() >= requiredToWin )
				{
					DBG("Player 2 wins");
					setDisplayText("Player 2 Wins\nWell Done!");
					winnersLabel->setVisible(true);
					
					// Disable the pause, stop and settings buttons
					playPauseButton->setEnabled(false);
					settingsButton->setEnabled(false);
					stopButton->setEnabled(false);
				}
			}
			else if (waitingForKeyPress && hasKeyBeenPressed)
			{
				DBG("Key has been pressed now");
				playPauseButton->setEnabled(true);
				settingsButton->setEnabled(true);
				stopButton->setEnabled(true);
				
				winnersLabel->setVisible(false);
				
				// Reset flags and then the game
				waitingForKeyPress = false;
				hasKeyBeenPressed = false;
				
				stopButton->triggerClick();
			}
		}
		
		
		//==============================================================================
		// ButtonClicked callbacks
		//==============================================================================		
		
		void buttonClicked (Button* buttonThatWasClicked)
		{
			if (buttonThatWasClicked == settingsButton)
			{
				// If the game is not already paused
				if ( playPauseButton->getToggleState() )
					// Pause the game first...
					playPauseButton->triggerClick();
				
				// ...then show the settings in a DialogWindow
				settingsComp->setSize (300, 200);
				DialogWindow::showModalDialog (T("Game Settings"),
											   settingsComp,
											   this,
											   Colours::azure,
											   true);
			}
			
			// Toggle the state of the play/pause button
			else if (buttonThatWasClicked == playPauseButton)
			{
				// Remove display label if present
				if (displayLabel->isVisible())
					removeDisplayText();				
				if (playPauseButton->getToggleState())
				{
					removeDisplayText();
					playPauseButton->setButtonText(T("Pause"));
					playSound(serveHitSound);
				}
				else if (! playPauseButton->getToggleState())
				{
					setDisplayText("Game Paused");
					playPauseButton->setButtonText(T("Play"));
				}
			}
			
			else if (buttonThatWasClicked == stopButton)
			{
				// Pause the game and change the display text to a more appropriate message
				playPauseButton->setToggleState(false, true);
				setDisplayText("Press play to\nPlay Again");
												
				// Reset the scores
				score1->setText(String(0), false);
				score2->setText(String(0), false);
				
				// Move the ball back to the center of the screen
				ballAngle = (0.5 + ((Random::getSystemRandom().nextFloat()) / 2));
				x = getWidth()/2 - 10;
				y = getHeight()/2 - 10;
				ball->setTopLeftPosition(x, y);
			}
		}
		
		
		//==============================================================================
		// KeyPress callbacks
		//==============================================================================
		
		bool keyPressed (const KeyPress &key, Component *originatingComponent)
		{
			if (waitingForKeyPress)
			{
				hasKeyBeenPressed = true;
				DBG("Key Pressed");
			}
			
			// Add key shortcuts for the three buttons
			if(key == KeyPress ('p'))
				playPauseButton->triggerClick();
			else if(key == KeyPress ('g'))
				settingsButton->triggerClick();
			else if(key == KeyPress ('s'))
				stopButton->triggerClick();
				
			return true;
		}
		
		
		//==============================================================================
		// Audio device callbacks
		//==============================================================================
		
		/**
			Audio Device Callback method to process audio.
		 
			Audio processing on the source player can be performed here. Currently this 
			is a gain change the level of which is obtained from the settings component.
			@see SettingsComp::volumeSlider
		*/
		void audioDeviceIOCallback (const float** inputChannelData,
									int totalNumInputChannels,
									float** outputChannelData,
									int totalNumOutputChannels,
									int numSamples)
		{		
			// pass the audio callback on to our player source
			audioSourcePlayer.audioDeviceIOCallback (inputChannelData, totalNumInputChannels, outputChannelData, totalNumOutputChannels, numSamples);
			
			// Adjust the volume level of the audio
//			audioSourcePlayer.setGain(volumeLevel);
			
			// Adjust the volume level of the audio
			float volumeLevel = settingsComp->volumeSlider->getValue();
			
			for(int i = 0; i < totalNumOutputChannels; i++)
			{
				float *outputData = outputChannelData[i];
				if (outputData != 0)
				{
					for(int j = 0; j < numSamples; j++)
					{
						outputData[j] *= volumeLevel;
					}
				}
			}
		}
		
		void audioDeviceAboutToStart (AudioIODevice* device)
		{
			audioSourcePlayer.audioDeviceAboutToStart (device);
		}
		
		void audioDeviceStopped()
		{
			audioSourcePlayer.audioDeviceStopped();
		}
		
		
		//==============================================================================
		// Custom functions:
		// These are used to simplify the main constructor and associated callbacks
		//==============================================================================
		
		/** Simplified way of playing a sound file.
			 
			This function checks to see if the audio file is valid before attempting to play it.
			This avoids crashes.
			
			@param soundToPlay		The position in the filePlayers array which determines the sound file to be played.
		*/
		void playSound (int soundToPlay)
		{
			AudioFilePlayer* filePlayer = filePlayers[soundToPlay];
			if (filePlayer != 0)
				filePlayer->startFromZero();
		}
		
		
		/** This simplifies setting the display text.
		 		 
			@param textToDisplay		The text to be displayed to the user passed as a string.
		 */
		void setDisplayText (String textToDisplay)
		{
			displayLabel->setText(textToDisplay, false);
			displayLabel->setVisible(true);
		}
		
		/** This simplifies removing the display text.
		 */
		void removeDisplayText (void)
		{
			displayLabel->setText(String::empty, false);
			displayLabel->setVisible(false);
		}
		
		
		/** This deals with moving the bats.
			It checks to see if a key is down and then moves the
			corresponding bat in the correct direction
		 */
		void moveBats(void)
		{
			if( playPauseButton->getToggleState() )
			{			
				KeyPress key_a('a');
				KeyPress key_z('z');
				
				float batSpeed = settingsComp->batSpeedSlider->getValue();
				
				// Check for up key
				if (KeyPress::isKeyCurrentlyDown (KeyPress::upKey))
				{
					if (bat2->getY() > 0)
						bat2->setTopLeftPosition(bat2->getX(), bat2->getY() - batSpeed);
				}
				// Check for down key
				else if (KeyPress::isKeyCurrentlyDown (KeyPress::downKey))
				{
					if (bat2->getBottom() < getHeight())
						bat2->setTopLeftPosition(bat2->getX(), bat2->getY() + batSpeed);
				}
				// Check for 'a' key
				if (KeyPress::isKeyCurrentlyDown (key_a.getKeyCode()))
				{
					if (bat1->getY() > 0)
						bat1->setTopLeftPosition(bat1->getX(), bat1->getY() - batSpeed);
				}
				// Check for 'z' key
				else if (KeyPress::isKeyCurrentlyDown (key_z.getKeyCode()))
				{
					if (bat1->getBottom() < getHeight())
						bat1->setTopLeftPosition(bat1->getX(), bat1->getY() + batSpeed);
				}
			}
		}
		
		
		/**	This deals with moving the ball and detecting collisions.
		 */
		void moveBall(void)
		{		
			// Resest ball bounced flags 
			bounced = false;
			leftSideHit = false;
			rightSideHit = false;
			
			// Find distance to move ball 
			if( playPauseButton->getToggleState() )
			{
				// Update the ball speed
				float ballSpeed = settingsComp->ballSpeedSlider->getValue();
				
				// This makes sure that after the game is paused the ball 
				// continues in the same direction as before
				if(dx != 0 && dy != 0)
				{
					prevdx = dx;
					prevdy = dy;
				}
				
				// Update ball movement
				if (prevdx > 0)
					dx = (ballSpeed);
				else
					dx = -(ballSpeed);
				
				if (prevdy > 0)
					dy = (ballSpeed * ballAngle);
				else
					dy = -(ballSpeed * ballAngle);
			}
			else
			{
				dx = 0;
				dy = 0;
			}
			
			// Set new current co-ordinates
			x += dx;
			y += dy;
			
			// Ball hits left side of window
			if (x < 0)
			{
				dx = fabsf (dx);
				leftSideHit = true;
			}
			
			// Ball hits right side of window
			if (x > (getWidth() - ball->getWidth()) )
			{
				dx = -fabsf (dx);
				rightSideHit = true;
			}
			
			// Ball hits top of window
			if (y < 0)
			{
				dy = fabsf (dy);
				bounced = true;
			}
			
			// Ball hits bottom of window
			if (y > (getHeight() - ball->getHeight()) )
			{
				dy = -fabsf (dy);
				bounced = true;
			}
			
			// If ball hits left bat
			if ((x <= bat1->getRight()) && (x >= bat1->getRight()-10)
				&& ((y + ball->getHeight()) >= bat1->getY())
				&& (y <= bat1->getBottom()))
			{
				playSound(batHitSound);
				dx = fabsf (dx);
			}
			
			// If ball hits right bat
			if ( ((x + ball->getWidth()) >= bat2->getX() && (x + ball->getWidth() <= bat2->getX()+10))
				&& ( ((y + ball->getHeight())) >= bat2->getY() )
				&& (y <= bat2->getBottom()))
			{
				playSound(batHitSound);
				dx = -fabsf (dx);
			}
			
			// Move ball to new co-ordinates
			ball->setTopLeftPosition ((int) x, (int) y);
		}
		//==============================================================================
	};

#endif//_MAINCOMPONENT_H_ 